package dbx

import (
	"fmt"
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/dictx"
	"gitee.com/zhongguo168a/gocodes/datax/mapx"
	"gitee.com/zhongguo168a/gocodes/myx/nowctrl"
	"strings"
	"sync"
)

type SaveItem struct {
	Opt Opt
	//// 保存项需要改变的文档数据
	//Document *ChangeDocument
	// 保存项的编号
	Ident    string
	Table    string
	TableKey string
	Database string

	// 最终的值
	SetDocument    map[string]interface{}
	CreateDocument map[string]interface{}
	UnsetDocument  map[string]interface{}

	LastSet   map[string]interface{}
	LastUnset map[string]interface{}
	// 创建时间
	CreateTime int
	mutex      sync.RWMutex
}

func (item *SaveItem) GetDocumentByCreate() (con map[string]interface{}) {
	item.mutex.Lock()
	defer item.mutex.Unlock()

	mset := item.SetDocument
	con = map[string]interface{}{
		"$set": mset,
	}
	return
}

func (item *SaveItem) GetDocumentByUpdate() (con map[string]interface{}) {
	con = map[string]interface{}{}
	item.mutex.Lock()
	defer item.mutex.Unlock()

	mset := map[string]interface{}{}
	munset := map[string]interface{}{}
	if item.SetDocument != nil {
		if item.CreateDocument != nil {
			deleteMap := mapx.DeleteByKeyMap(item.SetDocument, item.CreateDocument, false)
			walkDocMap(item.CreateDocument, mset, "", ".")
			for createPath, _ := range mset {
				m := mapx.MapByPath(deleteMap, "/"+strings.ReplaceAll(createPath, ".", "/"), true)
				mset[createPath] = m
			}
		}
		walkDocMap(item.SetDocument, mset, "", ".")
	}
	if item.UnsetDocument != nil {
		walkDocMap(item.UnsetDocument, munset, "", ".")
	}
	if len(mset) > 0 {
		con["$set"] = mset
		item.LastSet = datax.M{"$set": mset}
		fmt.Printf("$set %+v\n", mset)
	}
	if len(munset) > 0 {
		con["$unset"] = munset
		item.LastUnset = datax.M{"$unset": munset}
		fmt.Printf("$unset %+v\n", munset)
	}

	return
}

func (item *SaveItem) GetQuery() map[string]interface{} {
	return map[string]interface{}{
		"_id": item.TableKey,
	}
}

func (item *SaveItem) GetTableIdent() string {
	return item.Database + "/" + item.Table
}

func (item *SaveItem) GetObjectIdent() string {
	return item.Database + "/" + item.Table + "/" + item.Ident
}
func (item *SaveItem) SetOptDelete() {
	item.Opt = Opt_删除
	item.SetDocument = nil
	item.CreateDocument = nil
	item.UnsetDocument = nil
}

func (item *SaveItem) AddSetDocument(opt *OptItem) {
	if item.SetDocument == nil {
		item.SetDocument = map[string]interface{}{}
	}
	path := opt.Path
	if item.UnsetDocument != nil {
		hasUnset := IsPathInUnset(item.UnsetDocument, path)
		if hasUnset { // 当前路径的数据已经在删除状态，无需修改
			return
		}
	}
	switch data := opt.Data.(type) {
	case map[string]interface{}:
		m := mapx.MapByPath(item.SetDocument, path, false)
		if m == nil {
			mapx.SetByPath(item.SetDocument, path, data)
		} else {
			mapx.Copy(data, m)
		}
	default:
		if path != "/" {
			mapx.SetByPath(item.SetDocument, path, data)
		} else {
			panic("AddSetDocument: set path root value must map")
		}

	}
}
func (item *SaveItem) AddCreateDocument(opt *OptItem) error {
	if item.CreateDocument == nil {
		item.CreateDocument = map[string]interface{}{}
	}
	path := opt.Path
	if item.UnsetDocument != nil {
		if path != "/" {
			hasUnset := IsPathInUnset(item.UnsetDocument, path)
			if hasUnset {
				mapx.DeleteByKeyMap(item.UnsetDocument, mapx.CreateByPath(path), false)
			}
		}
	}

	if path != "/" {
		hasCreate := IsPathInUnset(item.CreateDocument, path)
		if !hasCreate {
			mapx.SetByPath(item.CreateDocument, path, nil)
		}
	}

	if path == "/" {
		item.SetDocument = opt.Data.(map[string]interface{})
	} else {
		if item.SetDocument == nil {
			item.SetDocument = map[string]interface{}{}
		}
		mapx.SetByPath(item.SetDocument, path, opt.Data)
	}

	return nil
}

// path所在的路径是否已经删除
func IsPathInUnset(unset map[string]interface{}, path string) bool {
	if unset == nil {
		return false
	}
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}
	path = path[1:]
	arr := strings.Split(path, "/")

	curmap := unset
	curkey := ""
	for {
		if len(arr) == 0 {
			return true
		}
		curkey = arr[0]
		arr = arr[1:]

		ir, ok := curmap[curkey]
		if ok == false {
			return false
		}
		if ir == nil {
			return true
		}
		curmap = ir.(map[string]interface{})
	}
	return false
}

func (item *SaveItem) AddUnsetDocument(opt *OptItem) {
	if item.UnsetDocument == nil {
		item.UnsetDocument = map[string]interface{}{}
	}
	path := opt.Path
	if item.CreateDocument != nil {
		hasCreate := IsPathInUnset(item.CreateDocument, path)
		if hasCreate { // 数据创建但未保存到数据库, 因此直接删除, 不会影响最后的数据
			mapx.DeleteByKeyMap(item.CreateDocument, mapx.CreateByPath(path), false)
			// 抵消掉创建，不需要添加到UnsetDocument
		} else {
			mapx.SetByPath(item.UnsetDocument, path, nil)
		}
	} else {
		mapx.SetByPath(item.UnsetDocument, path, nil)
	}
	if item.SetDocument != nil {
		mapx.DeleteByKeyMap(item.SetDocument, mapx.CreateByPath(path), false)
	}
}

func (item *SaveItem) UpdateByOpts(opts []*OptItem) error {
	item.mutex.Lock()
	defer item.mutex.Unlock()

	for _, val := range opts {
		err := item.updateByOpt(val)
		if err != nil {
		}
	}
	return nil
}

func (item *SaveItem) updateByOpt(opt *OptItem) error {

	switch opt.Opt {
	case DictOpt_删除:
		item.AddUnsetDocument(opt)
	case DictOpt_创建:
		item.AddCreateDocument(opt)
	case DictOpt_修改:
		item.AddSetDocument(opt)
	case DictOpt_替换:
		item.AddCreateDocument(opt)
	default:
	}
	return nil
}

// -----------------------------------------------------------------------------------
//  -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
func NewSaveDict() (obj *SaveDict) {
	obj = &SaveDict{}
	obj.IDictionary = dictx.New[string, *SaveItem]()
	return
}

type SaveDict struct {
	dictx.IDictionary[string, *SaveItem]
}

func (dict *SaveDict) GroupByTable() map[string][]*SaveItem {
	tables := map[string][]*SaveItem{}
	_ = dict.ForRange(func(key string, val *SaveItem) (err error) {
		tableIdent := val.GetTableIdent()
		arr := tables[tableIdent]
		arr = append(arr, val)
		tables[tableIdent] = arr
		return nil
	})
	return tables
}

// -----------------------------------------------------------------------------------
//  -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
func NewSaveResp() (obj *SaveResp) {
	obj = &SaveResp{}
	obj.SaveDict = NewSaveDict()
	return
}

type SaveResp struct {
	*SaveDict

	mutex sync.Mutex
}

func (resp *SaveResp) NewSaveItem(delayItem *DelayItem) *SaveItem {
	var (
		item *SaveItem
	)
	item = &SaveItem{
		Ident:      delayItem.Ident,
		TableKey:   delayItem.TableKey,
		Table:      delayItem.Table,
		Database:   delayItem.Database,
		CreateTime: nowctrl.Now(),
	}
	resp.Set(item.Ident, item)
	return item
}

func (resp *SaveResp) DictByGroup(group *SaveGroup) *SaveDict {
	dict := NewSaveDict()
	_ = group.ForRange(func(key string, val int) (err error) {
		saveItem := resp.MustGet(key)
		if saveItem == nil {
			// 如果保存项不存在，表示已经有另外的组处理过并删除掉
			return
		}
		dict.Set(key, saveItem)
		return nil
	})
	return dict
}
